<template>
    <!--  vue2中的html模板中必要要有一对根标签,vue组件的html模板中可以没有根标签-->
    <img alt="Vue logo" src="./assets/logo.png">
    <!--    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>-->
    <h1>{{count}}</h1>
    <button @click="updateCount">更新数据</button>
    <br>
    <button @click="updateA">setup和refAPI更新数据</button>
    <h2>{{a}}</h2>
    <hr>
    <h3>姓名:{{user.name}}</h3>
    <h3>年龄:{{user.age}}</h3>
    <h3>配偶:{{user.wife}}</h3>
    <button @click="updateUser">更新用户</button>
    <h3>MSG:{{msg}}</h3>
    <button @click="updateMsg">更新MSG</button>
    <hr>
    <!-- 显示的是ref对象.value, html中省略了.value-->
    <Child :msg="msg" :userAge="userAge" @emitHandler="emitHandler"/>
    <hr>
    <fieldset>
        <legend>姓名操作</legend>
        姓氏:<input type="text" placeholder="请输入姓氏" v-model="worker.firstName"/><br>
        名字:<input type="text" placeholder="请输入名字" v-model="worker.lastName"/><br>
    </fieldset>
    <fieldset>
        <legend>计算属性和监视</legend>
        姓名:<input type="text" placeholder="显示姓名" v-model="fullName1"/><br>
        姓名:<input type="text" placeholder="显示姓名" v-model="fullName2"/><br>
        姓名:<input type="text" placeholder="显示姓名" v-model="fullName3"/><br>
    </fieldset>
    <hr>
    <h2>自定义hook函数</h2>
    <h3>x:{{x}} y:{{y}}</h3>
    <h3 v-if="loading">正在加载中...</h3>
    <h3 v-else-if="errorMsg">{{errorMsg}}</h3>
    <ul v-else>
        <li>id:{{data.id}}</li>
        <li>address:{{data.address}}</li>
        <li>distance:{{data.distance}}</li>
    </ul>
    <ul v-for="item in data" :key="item.id">
        <li>id:{{item.id}}</li>
        <li>title:{{item.title}}</li>
        <li>price:{{item.price}}</li>
    </ul>
    <hr>
    <h2>toRefs的使用</h2>
    <h3>age:{{age}}</h3>
    <h3>idCard:{{idCard}}</h3>
    <h3>job:{{job}}</h3>
    <hr>
    <input type="text" ref="inputRef">
    <hr>
    <button @click="testToRef">testToRef</button>
    <hr>
    <input type="text" v-model="keyword">
    <h3>keyword:{{keyword}}</h3>
    <hr>
    <h2>provide和jnject</h2>
    <p>当前的颜色:{{color}}</p>
    <button @click="color='red'">红色</button>
    <button @click="color='yellow'">黄色</button>
    <button @click="color='green'">绿色</button>
    <hr>
    <Suspense>
        <template #default>
            <!--异步组件-->
        </template>
        <template v-slot:fallback>
            <!--loading内容-->
            <h2>loading</h2>
        </template>
    </Suspense>
</template>

<script lang="ts">
    // defineComponent:定义一个组件,内部可以传入一个配置对象
    import {
        defineComponent,
        ref,
        reactive,
        computed,
        watch,
        watchEffect,
        onMounted,
        toRefs,
        toRef,
        customRef,
        provide
    } from 'vue';
    import Child from "@/components/Child.vue";
    import useMousePosition from "@/hooks/useMousePosition";
    import useRequest from "@/hooks/useRequest";
    // 引入子组件
    import HelloWorld from './components/HelloWorld.vue';

    //定义接口,约束对象的类型
    interface AddressData {
        id: number;
        address: string;
        distance: string;
    }

    interface ProductData {
        id: string;
        title: string;
        price: number;
    }

    function userFeatureX() {
        const people = {
            idCard: 123,
            job: 'teacher'
        }
        return {
            ...toRefs(people)
        }
    }

    // 自定义hook防抖函数
    // value传入的数据,delay防抖间隔
    function useDebouncedRef<T>(value: T, delay = 200) {
        let timeOutId: number
        return customRef((track, trigger) => {
            return {
                // 返回的数据
                get() {
                    track()
                    return value
                },
                set(newVal: T) {
                    // 清理定时器
                    clearTimeout(timeOutId)
                    timeOutId = setTimeout(() => {
                        value = newVal
                        // 通知vue更新界面
                        trigger()
                    }, delay)
                }
            }
        })

    }

    // 暴露出去一个定义好的组件
    export default defineComponent({
        name: 'App', // 当前组件的名字
        // 注册组件
        components: {
            Child
        },
        data() {
            return {
                count: 0
            }
        },
        methods: {
            updateCount() {
                this.count++
            }
        },
        // setup是组合API的入口函数. 在beforeCreate()之前执行,只执行一次
        // setup在执行的时候,当前组件还没有创建出来,所以setup中不能使用this
        // setup、data、methods不要混合使用,methods可以访问setup提供的属性和方法,但setup不能访问data和methods
        setup() {
            // ref的作用:定义一个响应式的数据,返回一个ref对象,对象中有个value属性
            // 一般用来定义一个基本类型的响应数据
            const a = ref(0)
            const msg = ref('今天天气很好')

            function updateA() {
                a.value++
            }

            // reactive返回一个代理对象,深层次的监视
            const user = reactive({
                name: 'java',
                age: 18,
                wife: {
                    name: 'mysql',
                    age: 16,
                    cars: ['宝马', '奔驰', '奥迪']
                }
            })
            const updateUser = () => {
                user.name += "ee"
                user.age += 2
                user.wife.cars.push('宾利')
            }

            const updateMsg = () => {
                msg.value += '==='
            }

            const emitHandler = (v: any) => {
                msg.value += v
            }

            const worker = reactive({
                firstName: '东方',
                lastName: '不败'
            })

            // vue3中的计算属性如果只传入一个回调函数,表示getter方法
            const fullName1 = computed(() => {
                return worker.firstName + '_' + worker.lastName
            })

            const fullName2 = computed({
                get() {
                    return worker.firstName + '_' + worker.lastName
                },
                set(val: string) {
                    const names = val.split('_')
                    worker.firstName = names[0]
                    worker.lastName = names[1]
                }
            })

            // 监视
            const fullName3 = ref('')
            watch(worker, ({firstName, lastName}) => {
                fullName3.value = firstName + '_' + lastName
            }, {immediate: true, deep: true}) // immediate默认会执行一次监视,deep深度监视

            // watchEffect不需要配置immediate,默认就会进行监视
            // watchEffect(()=>{
            //     fullName3.value = worker.firstName + '_' + worker.lastName
            // })

            // 监视fullName3的数据,改变firstName,lastName
            watchEffect(() => {
                const names = fullName3.value.split('_')
                worker.firstName = names[0]
                worker.lastName = names[1]
            })

            // watch可以监视多个对象,监视深层属性()=>x
            watch([() => worker.firstName, () => worker.lastName, fullName3], () => {
                console.log('====')
            })

            // 获取鼠标点击位置页面坐标
            const {x, y} = useMousePosition()

            // 自定义hook函数, 泛型的使用
            // const {loading, data, errorMsg} = useRequest<AddressData>('/data/address.json')
            const {loading, data, errorMsg} = useRequest<ProductData[]>('/data/products.json')
            watch(data, () => {
                if (data.value) {
                    console.log(data.value.length);
                }
            })

            // toRefs的作用:可以把一个响应式的对象转换成普通对象,该普通对象的每个属性都是ref对象
            const user1 = toRefs(user)
            const {name, age} = toRefs(user)
            setInterval(() => {
                // user.age +=1
                // user1.age.value +=1
                age.value += 1
            }, 1000)
            const {idCard, job} = userFeatureX()

            // ref获取元素
            const inputRef = ref<HTMLElement | null>(null)
            onMounted(() => {
                inputRef.value && inputRef.value.focus() // 自动获取焦点
            })

            // 把响应式数据user对象中的某个属性age变成了ref对象
            const userAge = toRef(user, 'age')
            // 把响应式数据对象中的某个属性使用ref进行包装,变成一个ref对象
            const userName = ref(user.name)
            const testToRef = () => {
                // userAge.value +=1  // 界面同步响应更新
                userName.value += '==' // 界面不更新
            }

            // 自定义customRef
            const keyword = useDebouncedRef('abc', 100)

            // provide和inject实现跨层级间的通信(祖孙)
            const color = ref('red')
            // 提供数据
            provide('color', color)

            return {
                a,
                updateA,
                user,
                updateUser,
                msg,
                updateMsg,
                emitHandler,
                worker,
                fullName1,
                fullName2,
                fullName3,
                x,
                y,
                loading,
                data,
                errorMsg,
                //...user // 不是响应式的数据 --->{name:xx, age:xx}
                // ...user1  // toRefs返回的对象
                name,
                age,
                idCard,
                job,
                inputRef,
                testToRef,
                userAge,
                keyword,
                color
            }

        }
    });
</script>

<style>
    #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        /*text-align: center;*/
        color: #2c3e50;
        margin-top: 60px;
    }
</style>
